package com.quwan.component;


import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayFundTransUniTransferModel;
import com.alipay.api.domain.Participant;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.app.models.AlipayTradeAppPayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.quwan.app.service.MemberService;
import com.quwan.app.service.PaymentService;
import com.quwan.app.service.PrePaymentInterface;
import com.quwan.app.vo.pay.*;
import com.quwan.common.enums.PayStatusEnum;
import com.quwan.common.exception.PayException;
import com.quwan.config.yml.AppAlipayConfig;
import com.quwan.config.yml.WxpayConfig;
import com.quwan.constans.AppConst;
import com.quwan.entity.TMember;
import com.quwan.entity.TSystemPrePay;
import com.quwan.mapper.TSystemPrePayMapper;
import com.quwan.utils.PayUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.function.Predicate;


/**
 * 非必要 该类不要继承或者实现其他接口
 * 反射该类获取方法的时候 不检查父类方法
 *
 * @author quan
 * @date 2021-07-21 15:18
 */
@Component("payment")
public class PaymentComponent {
    private static final Logger log = LoggerFactory.getLogger(PaymentComponent.class.getName());
    private static final String WX_SUCCESS = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
    private static final String WX_FAIL_NULL = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[报文为空]></return_msg></xml>";
    private static final String WX_FAIL_ERR = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[验签失败]></return_msg></xml>";
    private static final String SUCCESS = "SUCCESS";
    private static final String FAIL = "FAIL";
    @Autowired
    private RestTemplate restTemplate;
    @Qualifier("wxCertRestTemplate")
    @Autowired
    private RestTemplate wxCertRestTemplate;
    @Autowired
    private TSystemPrePayMapper systemPrePayMapper;
    @Autowired
    private MemberService memberService;
    @Autowired
    private AppAlipayConfig appAlipayConfig;
    @Autowired
    private WxpayConfig wxpayConfig;
    @Autowired
    private PaymentService paymentService;
    private AlipayConfig alipayConfig;

    @PostConstruct
    public void initPaymentParams() {
        AlipayConfig alipayConfig = new AlipayConfig();
        //网关
        alipayConfig.setServerUrl(appAlipayConfig.getAlipayGateway());
        //appid
        alipayConfig.setAppId(appAlipayConfig.getAppId());
        //应用私钥
        alipayConfig.setPrivateKey(appAlipayConfig.getAppPrivateKey());
        //应用公钥证书路径
        alipayConfig.setAppCertPath(appAlipayConfig.getAppCertPath());
        //支付宝根证书路径
        alipayConfig.setRootCertPath(appAlipayConfig.getAlipayRootCertPath());
        //支付宝公钥证书路径
        alipayConfig.setAlipayPublicCertPath(appAlipayConfig.getAlipayCertPath());
        alipayConfig.setCharset(appAlipayConfig.getCharset());
        alipayConfig.setSignType(appAlipayConfig.getSignType());
        this.alipayConfig = alipayConfig;
        Factory.setOptions(getOptions(appAlipayConfig.getNotifyUrl()));
    }

    //微信支付 支付订单异步回调处理
    protected Function<Map<String, String>, String> wxBuyItOrder() {
        return _wxProcess(paymentService::updOrderPayFinishInfo);
    }

    //微信支付 支付vip异步回调处理
    protected Function<Map<String, String>, String> wxBuyItVip() {
        return _wxProcess(paymentService::updVipPayFinishInfo);
    }

    //微信支付 余额充值 异步回调处理
    protected Function<Map<String, String>, String> wxBuyItBalance() {
        return _wxProcess(paymentService::updRechargeBalanceFinishInfo);
    }

    //微信支付 派单 异步回调处理
    protected Function<Map<String, String>, String> wxPayDispatchOrder() {
        return _wxProcess(paymentService::updDispatchOrderPayFinishInfo);
    }

    //微信支付 派单定金 异步回调处理
    protected Function<Map<String, String>, String> wxDispatchBefore() {
        return _wxProcess(paymentService::updBeforeDispatchOrderPayFinishInfo);
    }

    //支付宝 支付订单异步回调处理
    protected Function<Map<String, String>, String> zfbBuyItOrder() {
        return _zfbProcess(paymentService::updOrderPayFinishInfo);
    }

    //支付宝 派单支付订单
    // 异步回调处理
    protected Function<Map<String, String>, String> zfbPayDispatchOrder() {
        return _zfbProcess(paymentService::updDispatchOrderPayFinishInfo);
    }

    //支付宝 派单定金支付
    // 异步回调处理
    protected Function<Map<String, String>, String> zfbDispatchBefore() {
        return _zfbProcess(paymentService::updBeforeDispatchOrderPayFinishInfo);
    }

    //支付宝 支付vip异步回调处理
    protected Function<Map<String, String>, String> zfbBuyItVip() {
        return _zfbProcess(paymentService::updVipPayFinishInfo);
    }

    //支付宝 余额充值 异步回调处理
    protected Function<Map<String, String>, String> zfbBuyItBalance() {
        return _zfbProcess(paymentService::updRechargeBalanceFinishInfo);
    }

    /**
     * 支付宝 oauth2
     *
     * @param code 授权页获取到的code
     * @return
     * @throws AlipayApiException
     */
    public String getAliUserId(String code) throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(code);
        AlipaySystemOauthTokenResponse response = alipayClient.certificateExecute(request);
        if (response.isSuccess()) {
            return response.getUserId();
        } else {
            log.info("getAliUserId 调用失败");
            return null;
        }
    }

    /**
     * 微信下单
     */
    protected PrePaymentInterface wxPay() {
        return prePayParams -> {
            String orderId = prePayParams.getOrderId();
            String money = prePayParams.getMoney();
            String desc = prePayParams.getDescription();
            SortedMap<String, String> paramMap = new TreeMap<String, String>();
            paramMap.put("appid", wxpayConfig.getAppId());
            paramMap.put("mch_id", wxpayConfig.getMchId());
            paramMap.put("nonce_str", PayUtil.makeUUID(32));
            paramMap.put("body", AppConst.APP_NAME + desc);
            paramMap.put("out_trade_no", orderId);
            paramMap.put("total_fee", PayUtil.moneyToIntegerStr(money));//微信单位是分 需要转换一下
            paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
            paramMap.put("notify_url", wxpayConfig.getOrderNotify());
            paramMap.put("trade_type", "APP");
            paramMap.put("attach", prePayParams.getCustom());
            paramMap.put("sign", PayUtil.createSign(paramMap, wxpayConfig.getApiPrivateKey()));
            String xmlData;
            try {
                xmlData = PayUtil.mapToXml(paramMap);
            } catch (Exception e) {
                throw new RuntimeException("wxPrePay mapToXml --> " + e.getMessage());
            }
            log.debug("call wxPrePay Api ::{}", xmlData);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(
                    wxpayConfig.getPayInterfaceUrl(), xmlData, String.class);

            log.info("wxPrePay response::" + responseEntity);
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                throw new IllegalArgumentException("wxPrePay fail" + responseEntity.getBody());
            }
            Map<String, String> map = PayUtil.xmlStrToMap(responseEntity.getBody());

            return new WxPrePayment(
                    wxpayConfig.getAppId(),
                    wxpayConfig.getMchId(),
                    map.get("prepay_id"),
                    wxpayConfig.getApiPrivateKey(),
                    "Sign=WXPay");
        };
    }

    /**
     * 支付宝下单
     *
     * @param prePayParams 支付参数
     * @return
     */
    protected PrePaymentInterface aliPay() {
        return (prePayParams) -> {
            String description = prePayParams.getDescription();
            String orderId = prePayParams.getOrderId();
            String money = prePayParams.getMoney();
            try {
                // 2. 发起API调用（以创建当面付收款二维码为例）
                // 3. 处理响应或异常
                AlipayTradeAppPayResponse response = Factory.Payment.App().optional("passback_params", prePayParams.getCustom()).pay(description, orderId, money);
                log.info("aliPay response::{}", response.body);
                if (ResponseChecker.success(response))
                    return new ZFBPrePayment(response.getBody());
                return null;
            } catch (Exception e) {
                log.error("aliPay exception，cause by::{}", e.getMessage());
                throw new RuntimeException(e.getMessage(), e);
            }
        };
    }

    /**
     * preId 订单id  -  时间戳
     *
     * @param prePayParams 支付参数
     * @return
     */
    public PrePaymentInterface balancePay() {
        return prePayParams -> {
            Long mId = Long.parseLong(prePayParams.getMId());
            String money = prePayParams.getMoney();
            String orderId = prePayParams.getOrderId();
            String status;
            String preId = "";
            //删除用户一些未支付的预支付信息
            systemPrePayMapper.delete(
                    new QueryWrapper<>(
                            new TSystemPrePay()
                                    .setMId(mId)
                                    .setStatus(PayStatusEnum.NOT.getType())
                    )
            );
            TMember member = memberService.getMemberMoneyInfo(mId);
            Assert.notNull(member, "member info null");
            BigDecimal balance = member.getBalance();
            BigDecimal payMoney = new BigDecimal(money);
            //余额大于等于支付金额
            if (balance.compareTo(payMoney) >= 0) {
                status = "1";
                preId = (Long.parseLong(orderId) - System.currentTimeMillis()) + "";
                //预支付信息生成
                int row =
                        systemPrePayMapper
                                .insert(
                                        new TSystemPrePay()
                                                .setPreId(Long.parseLong(preId))
                                                .setMId(member.getId())
                                                .setTradNo(Long.parseLong(orderId))
                                                .setMoney(payMoney));

                if (row != 1)
                    status = "-1";
            } else
                status = "0"; //余额不足
            return new BalancePrePayment(preId, status, orderId, payMoney.toString());
        };
    }

    //微信转账到个人
    protected Function<TransferParams, TransferResult> wxTrans() {
        return transferParams -> {
            String desc = transferParams.getDesc();
            try {
                desc = desc == null ? new String(transferParams.getId().getBytes("utf-8")) : new String(desc.getBytes("utf-8"));
            } catch (UnsupportedEncodingException e) {
            }
            SortedMap<String, String> paramMap = new TreeMap<String, String>();
            paramMap.put("mch_appid", wxpayConfig.getAppId());
            paramMap.put("mchid", wxpayConfig.getMchId());
            paramMap.put("nonce_str", PayUtil.makeUUID(32));
            paramMap.put("partner_trade_no", transferParams.getId());
            paramMap.put("openid", transferParams.getToUser());
            paramMap.put("check_name", "NO_CHECK");
            paramMap.put("amount", PayUtil.moneyToIntegerStr(transferParams.getMoney()));//单位分 需要转换一下
            paramMap.put("desc", desc);
            paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
            paramMap.put("sign", PayUtil.createSign(paramMap, wxpayConfig.getApiPrivateKey()));
            String xmlData;
            try {
                xmlData = PayUtil.mapToXml(paramMap);
            } catch (Exception e) {
                throw new RuntimeException("wxTransfer mapToXml --> err");
            }
            log.info("call wxTransfer Api ::{}", xmlData);
            ResponseEntity<String> responseEntity = wxCertRestTemplate.postForEntity(
                    wxpayConfig.getTransferInterfaceUrl(), xmlData, String.class);

            log.debug("wxTransfer response::" + responseEntity);
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                throw new IllegalArgumentException("wxTransfer err" + responseEntity.getBody());
            }
            Map<String, String> wxres = PayUtil.xmlStrToMap(responseEntity.getBody());
            TransferResult result = new TransferResult();
            if (wxres != null
                    && SUCCESS.equalsIgnoreCase(wxres.get("return_code"))
                    && SUCCESS.equalsIgnoreCase(wxres.get("result_code"))) {
                log.info("wx转账成功:wxOpenId::{},money::{}", transferParams.getToUser(), transferParams.getMoney());
                return result
                        .setTransactionId(wxres.get("payment_no"))
                        .setDesc(transferParams.getDesc())
                        .setStatus(true)
                        .setErrCode("0")
                        .setTradeNO(wxres.get("partner_trade_no"));
            } else {
                //  String errInfo = wxres.get("err_code") + "::" + wxres.get("err_code_des");
                log.error("wx转账失败::{}", responseEntity.getBody());
                return result
                        .setStatus(false)
                        .setErrCode(responseEntity.getBody())
                        .setTradeNO(transferParams.getId());
            }

        };
    }


    //支付宝转账到个人
    protected Function<TransferParams, TransferResult> aliTrans() {
        return transferParams -> {
            AlipayFundTransUniTransferRequest transferRequest = new AlipayFundTransUniTransferRequest();
            AlipayFundTransUniTransferModel transferModel = new AlipayFundTransUniTransferModel();
            //收款方信息
            Participant payeeInfo = new Participant();
            payeeInfo.setIdentity(transferParams.getToUser());
            payeeInfo.setIdentityType("ALIPAY_USER_ID");
            //付款方信息
            Participant payerInfo = new Participant();
            //用商户号昵称
            payerInfo.setMerchantUserInfo("merchant_user_nickname");
            transferModel.setTransAmount(transferParams.getMoney());
            transferModel.setPayeeInfo(payeeInfo);
            transferModel.setPayerInfo(payerInfo);
            transferModel.setOrderTitle(transferParams.getDesc());
            transferModel.setOutBizNo(transferParams.getId());
            transferModel.setProductCode("TRANS_ACCOUNT_NO_PWD");
            transferModel.setBizScene("DIRECT_TRANSFER");

            transferRequest.setBizModel(transferModel);

            try {
                TransferResult result = new TransferResult();
                AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
                AlipayFundTransUniTransferResponse transferResponse = alipayClient.certificateExecute(transferRequest);
                if (transferResponse.isSuccess() && SUCCESS.equalsIgnoreCase(transferResponse.getStatus())) {
                    //成功
                    String orderId = transferResponse.getOutBizNo();
                    String transactionId = transferResponse.getPayFundOrderId();
                    log.info("zfb 转账成功::toUser::{},money::{}", transferParams.getToUser(), transferParams.getMoney());
                    return result
                            .setStatus(true)
                            .setErrCode("0")
                            .setTradeNO(orderId)
                            .setTransactionId(transactionId)
                            .setDesc(transferParams.getDesc());

                } else {
                    log.error("zfb 转账失败::{}", JSONObject.toJSONString(transferResponse));
                    return result
                            .setStatus(false)
                            .setTradeNO(transferParams.getId())
                            .setErrCode(transferResponse.getSubCode() + "::" + transferResponse.getSubMsg());
                }
            } catch (AlipayApiException e) {
                log.error("AlipayApiException ::{}", e);
                throw new PayException(e.getMessage());
            }

        };
    }

    //wx 支付异步通知 处理实例
    private Function<Map<String, String>, String> _wxProcess(Predicate<NotifyProcessParams> process) {
        return map -> {
            String returnCode = map.get("return_code");
            if (SUCCESS.equalsIgnoreCase(returnCode)) {
                String resultCode = map.get("result_code");
                if (SUCCESS.equals(resultCode)) {
                    //验签
                    _wxAuthSign(map);
                    //处理业务
                    String orderId = map.get("out_trade_no");
                    String transactionId = map.get("transaction_id");
                    BigDecimal payPrice = new BigDecimal(map.get("total_fee")).divide(AppConst.b100, 2, 4); //微信单位 分 需要转换一下
                    //业务是否处理成功
                    if (process.test(new NotifyProcessParams(orderId, transactionId, payPrice.toString()))) {
                        log.info("wx 支付流程处理成功");
                        return WX_SUCCESS;
                    } else {
                        log.error("业务处理失败-->orderId：{}", orderId);
                        return WX_FAIL_ERR;
                    }

                } else {
                    log.error("交易失败::{}", map.get("return_msg"));
                    return WX_FAIL_NULL;
                }
            } else {
                log.error("通信失败::{}", map.get("return_msg"));
                return WX_FAIL_NULL;
            }

        };

    }

    //zfb 支付异步通知 处理实例
    private Function<Map<String, String>, String> _zfbProcess(Predicate<NotifyProcessParams> process) {
        return map -> {
            Boolean sign = false;
            try {
                sign = Factory.Payment.Common().verifyNotify(map);
            } catch (Exception e) {
                log.error("zfbBuyItOrder 验签异常::{},params::{}", e.getMessage(), map);
            }
            if (sign) {
                String tradeStatus = map.get("trade_status");
                if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus)) {
                    String orderId = map.get("out_trade_no");
                    String transactionId = map.get("trade_no");
                    String money = map.get("total_amount");

                    if (process.test(new NotifyProcessParams(orderId, transactionId, money))) {
                        log.info("zfb支付流程处理成功");
                        return SUCCESS.toLowerCase();
                    }
                    log.info("业务处理失败");
                    return FAIL.toLowerCase();
                }
                log.error("支付宝交易失败::{}", map);
            }
            log.error("支付宝验签失败::{}", map);
            return FAIL.toLowerCase();
        };
    }

    //支付宝支付全局参数配置
    private Config getOptions(String notify) {
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = alipayConfig.getServerUrl().replace("https://", "");
        config.signType = alipayConfig.getSignType();
        config.appId = alipayConfig.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayConfig.getPrivateKey();

        //注：证书文件路径支持设置为文件系统中的路径或CLASS_PATH中的路径，优先从文件系统中加载，加载失败后会继续尝试从CLASS_PATH中加载
        config.merchantCertPath = alipayConfig.getAppCertPath(); //"<-- 请填写您的应用公钥证书文件路径，例如：/foo/appCertPublicKey_2019051064521003.crt -->"
        config.alipayCertPath = alipayConfig.getAlipayPublicCertPath();//"<-- 请填写您的支付宝公钥证书文件路径，例如：/foo/alipayCertPublicKey_RSA2.crt -->"
        config.alipayRootCertPath = alipayConfig.getRootCertPath();//"<-- 请填写您的支付宝根证书文件路径，例如：/foo/alipayRootCert.crt -->"
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        // config.alipayPublicKey = appAlipayConfig.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = notify;
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        //  config.encryptKey = ZFBSecret;
        return config;
    }


    //微信异步回调验签
    private void _wxAuthSign(Map<String, String> map) {
        SortedMap<String, String> params = new TreeMap<>();
        params.putAll(map);
        String sign = PayUtil.createSign(params, wxpayConfig.getApiPrivateKey());
        if (!sign.equals(map.get("sign"))) {
            log.error("_wxAuthSign not matches sign::{},params::{}", sign, params);
            throw new IllegalArgumentException("FAIL::_wxAuthSign ");
        }

    }


}
